/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef UART_REG_ACCESS_H
#define UART_REG_ACCESS_H

/*!
 * @file uart_reg_access.h
 * @brief This file declares or defines uart register access functions
 */

/*******Includes***************************************************************/
#include "uart_drv.h"

/*******Definitions************************************************************/
#define UART_CFG_TE_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << UART_UCR_TEN_Pos)) & UART_UCR_TEN_Msk)
#define UART_CFG_RE_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << UART_UCR_REN_Pos)) & UART_UCR_REN_Msk)
#define UART_CFG_TDMA_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << UART_UCR_TDMAEN_Pos)) & UART_UCR_TDMAEN_Msk)
#define UART_CFG_RDMA_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << UART_UCR_RDMAEN_Pos)) & UART_UCR_RDMAEN_Msk)
#define UART_CFG_RIDLE_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << UART_UCR_RIDLEN_Pos)) & UART_UCR_RIDLEN_Msk)
#define UART_BAUD_SCALED_SHIFT (5U)
/*!
 *  @brief Type of UART status flags
 */
typedef enum {
    UART_TX_COMPLETE = 0x00U,       /*!< Tx transfer complete flag */
    UART_TX_FULL,                   /*!< Tx data fifo full flag */
    UART_TX_EMPTY,                  /*!< Tx data fifo empty flag */
    UART_NOSIE_ERROR,               /*!< Noise error flag */
    UART_PARITY_ERROR,              /*!< Parity error flag */
    UART_FRAME_ERROR,               /*!< Frame error flag */
    UART_RX_NOT_EMPTY,              /*!< Rx data fifo not empty flag */
    UART_RX_OVERFLOW,               /*!< Rx data fifo overflow error flag */
    UART_IDLE,                      /*!< Rx idle flag */
    UART_LIN_SYNC_ERROR   = 0x10U,  /*!< LIN sync filed error flag */
    UART_LIN_BREAK_DETECT = 0x11U,  /*!< LIN break detected flag */
    UART_LIN_BUS_ERROR              /*!< LIN bus error flag */
} uart_status_t;

/*!
 *  @brief Type of UART interrupt type
 */
typedef enum {
    UART_INT_TX_COMPLETE = 0x00U,   /*!< Tx transfer complete interrupt */
    UART_INT_TX_FULL,               /*!< Tx data fifo full interrupt */
    UART_INT_TX_EMPTY,              /*!< Tx data fifo empty interrupt */
    UART_INT_NOSIE_ERROR,           /*!< Noise error interrupt */
    UART_INT_PARITY_ERROR,          /*!< Parity error interrupt */
    UART_INT_FRAME_ERROR,           /*!< Frame error interrupt */
    UART_INT_RX_NOT_EMPTY,          /*!< Rx data fifo not empty interrupt */
    UART_INT_RX_OVERFLOW,           /*!< Rx data fifo overflow error interrupt */
    UART_INT_IDLE                   /*!< Rx idle interrupt */
} uart_inttype_t;

/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Set the uart baudrate oversampling ratio
 *
 * @param[in] regBase: Uart register base address
 * @param[in] overSamplingRate: Oversampling rate(UART_OVERSAMPLE_16/UART_OVERSAMPLE_4)
 * @return None
 */
static inline void UART_REG_SetOversamplingRate(UART_Type* regBase, uart_oversample_rate_t overSamplingRate)
{
    uint32_t regValue = regBase->BAUD;
    regValue &= ~(UART_BAUD_BROSR_Msk);
    regValue |= ((uint32_t)overSamplingRate << UART_BAUD_BROSR_Pos) & UART_BAUD_BROSR_Msk;
    regBase->BAUD = regValue;
}

/*!
 * @brief Get the uart baudrate oversampling ratio
 *
 * @param[in] regBase: Uart register base address
 * @return OverSamplingRate
 *         - UART_OVERSAMPLE_16: 16x oversampling, baud = fclk/[(BRINT+BRDEC>>5) * 16]
 *         - UART_OVERSAMPLE_4:  4x oversampling   baud = fclk/[(BRINT+BRDEC>>5) * 4]
 */
static inline uart_oversample_rate_t UART_REG_GetOversamplingRate(const UART_Type* regBase)
{
    return (uart_oversample_rate_t)((regBase->BAUD &  UART_BAUD_BROSR_Msk) >> UART_BAUD_BROSR_Pos);
}

/*!
 * @brief Set the uart baudrate divisor
 *
 * @param[in] regBase: Uart register base address
 * @param[in] baudDivisor: Baudrate divisor
 * @return None
 */
static inline void UART_REG_SetBaudRateDivisor(UART_Type* regBase, uint32_t baudDivisor)
{
    uint32_t regValue = regBase->BAUD;
    uint32_t intValue = baudDivisor >> UART_BAUD_SCALED_SHIFT;
    regValue &= ~(UART_BAUD_BRINT_Msk);
    regValue |= (intValue << UART_BAUD_BRINT_Pos) & UART_BAUD_BRINT_Msk;

    uint32_t decimalValue = (baudDivisor - (intValue << UART_BAUD_SCALED_SHIFT));
    regValue &= ~(UART_BAUD_BRDEC_Msk);
    regValue |= (decimalValue << UART_BAUD_BRDEC_Pos) & UART_BAUD_BRDEC_Msk;

    regBase->BAUD = regValue;
}

/*!
 * @brief Get the uart baudrate oversampling ratio
 *
 * @param[in] regBase: Uart register base address
 * @return Baudrate divisor
 */
static inline uint32_t UART_REG_GetBaudRateDivisor(const UART_Type* regBase)
{
    uint32_t intValue = (regBase->BAUD & UART_BAUD_BRINT_Msk) >> UART_BAUD_BRINT_Pos;
    uint32_t decimalValue = (regBase->BAUD & UART_BAUD_BRDEC_Msk) >> UART_BAUD_BRDEC_Pos;

    return (intValue << UART_BAUD_SCALED_SHIFT) + decimalValue;
}

/*!
 * @brief Get the BRINT value of the BAUD register
 *
 * @param[in] regBase: Uart register base address
 * @return Return the BRINT field value
 */
static inline uint32_t UART_REG_GetBAUD(const UART_Type* regBase)
{
    return (regBase->BAUD & UART_BAUD_BRINT_Msk);
}

/*!
 * @brief Set the uart data wordlength
 *
 * @param[in] regBase: Uart register base address
 * @param[in] wordLength: Number of bits in a character(7/8/9bits)
 * @return None
 */
static inline void UART_REG_SetWordLength(UART_Type* regBase, uart_word_length_t wordLength)
{
    uint32_t regValue = regBase->UCR;
    regValue &= ~(UART_UCR_DWIDTH_Msk);
    regValue |= ((uint32_t)wordLength << UART_UCR_DWIDTH_Pos) & UART_UCR_DWIDTH_Msk;
    regBase->UCR = regValue;
}

/*!
 * @brief Set the uart parity model
 *
 * @param[in] regBase: Uart register base address
 * @param[in] parityMode: Uart parity mode(disable/even/odd)
 * @return None
 */
static inline void UART_REG_SetParityModel(UART_Type* regBase,  uart_parity_mode_t parityMode)
{
    uint32_t regValue = regBase->UCR;
    regValue &= ~(UART_UCR_PEN_Msk);
    regValue |= (((uint32_t)parityMode >> 1U) << UART_UCR_PEN_Pos) & UART_UCR_PEN_Msk;

    regValue &= ~(UART_UCR_PTYPE_Msk);
    regValue |= (((uint32_t)parityMode & 1U) << UART_UCR_PTYPE_Pos) & UART_UCR_PTYPE_Msk;

    regBase->UCR = regValue;
}

/*!
 * @brief Set the number of stop bits
 *
 * @param[in] regBase: Uart register base address
 * @param[in] stopBit: The number of stop bits(1/2bits)
 * @return None
 */
static inline void UART_REG_SetStopBitCount(UART_Type* regBase, uart_stop_bit_count_t stopBit)
{
    uint32_t regValue = regBase->UCR;
    regValue &= ~(UART_UCR_STP_Msk);
    regValue |= ((uint32_t)stopBit << UART_UCR_STP_Pos) & UART_UCR_STP_Msk;
    regBase->UCR = regValue;
}

/*!
 * @brief Set the uart interrupt enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] intType: Interrupt type
 * @param[in] enable: Enable flag
 *            - true: Enable specified uart interrupt
 *            - false: Disable specified uart interrupt
 * @return None
 */
static inline void UART_REG_SetIntEnable(UART_Type* regBase, uart_inttype_t intType, bool enable)
{
    uint32_t intTypeBit = (uint32_t)intType;

    if (enable) {
        regBase->IER |= 1U << intTypeBit;
    } else {
        regBase->IER &= ~((uint32_t)(1U << intTypeBit));
    }
}

/*!
 * @brief Whether the uart interrupt is enabled
 *
 * @param[in] regBase: Uart register base address
 * @param[in] intType: interrupt type
 * @return true: The uart interrupt is enabled
 *         false: The uart interrupt is disabled
 */
static inline bool UART_REG_IsIntEnabled(const UART_Type* regBase, uart_inttype_t intType)
{
    uint32_t regValue = regBase->IER;

    return (regValue >> (uint32_t)intType) & 0x01U;
}

/*!
 * @brief Get uart status flag
 *
 * @param[in] regBase: Uart register base address
 * @param[in] statusFlag: Status flag
 * @return true: uart status flag is set to 1
 *         false: uart status flag is set to 0
 */
static inline bool UART_REG_GetStatusFlag(const UART_Type* regBase, uart_status_t statusFlag)
{
    uint32_t regValue = regBase->SR;

    return (regValue >> (uint32_t)statusFlag) & 0x01U;
}

/*!
 * @brief Clear uart status flag
 *
 * @param[in] regBase: Uart register base address
 * @param[in] statusFlag: Status flag
 * @return None
 */
static inline void UART_REG_ClearStatusFlag(UART_Type* regBase, uart_status_t statusFlag)
{
    regBase->SR = 1U << (uint32_t)statusFlag;
}


/*!
 * @brief Whether the uart tx is enabled
 *
 * @param[in] regBase: Uart register base address
 * @return true: The uart tx is enabled
 *         false: The uart tx is disabled
 */
static inline bool UART_REG_IsTxEnable(const UART_Type* regBase)
{
    return ((regBase->UCR & UART_UCR_TEN_Msk) >> UART_UCR_TEN_Pos);
}

/*!
 * @brief Set uart tx enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart tx
 *            - false: Disable uart tx
 * @return None
 */
static inline void UART_REG_SetTxEnable(UART_Type* regBase, bool enable)
{
    regBase->UCR = (regBase->UCR & ~UART_UCR_TEN_Msk) | UART_CFG_TE_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Set uart rx enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart rx
 *            - false: Disable uart rx
 * @return None
 */
static inline void UART_REG_SetRxEnable(UART_Type* regBase, bool enable)
{
    regBase->UCR = (regBase->UCR & ~UART_UCR_REN_Msk) | UART_CFG_RE_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Set uart rx DMA enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart tx DMA function
 *            - false: Disable uart tx DMA function
 * @return None
 */
static inline void UART_REG_SetTxDMAEnable(UART_Type* regBase, bool enable)
{
    regBase->UCR = (regBase->UCR & ~UART_UCR_TDMAEN_Msk) | UART_CFG_TDMA_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Set uart rx DMA enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart rx DMA function
 *            - false: Disable uart rx DMA function
 * @return None
 */
static inline void UART_REG_SetRxDMAEnable(UART_Type* regBase, bool enable)
{
    regBase->UCR = (regBase->UCR & ~UART_UCR_RDMAEN_Msk) | UART_CFG_RDMA_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Write data to the uart data register
 *
 * @param[in] regBase: Uart register base address
 * @param[in] data: Data to be written
 * @return None
 */
static inline void UART_REG_PutData(UART_Type* regBase, uint16_t data)
{
    regBase->DATA = data & UART_DATA_DATA_Msk;
}

/*!
 * @brief Read the data in the uart data register
 *
 * @param[in] regBase: Uart register base address
 * @return The data read from uart data register
 */
static inline uint16_t UART_REG_GetData(const UART_Type* regBase)
{
    return (uint16_t)(regBase->DATA & UART_DATA_DATA_Msk);
}

/*!
 * @brief Get uart data register address
 *
 * @param[in] regBase: Uart register base address
 * @return Data register address
 */
static inline uint32_t UART_REG_GetDataAddress(const UART_Type* regBase)
{
    return (uint32_t)(&regBase->DATA);
}

/*!
 * @brief Set uart all error interrupts enable(rx overflow,frame error,noise,parity error)
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart all error interrupts
 *            - false: Disable uart all error interrupts
 * @return None
 */
void UART_REG_SetErrorInts(UART_Type* regBase, bool enable);

/*!
 * @brief Clear uart all error interrupts enable(rx overflow,frame error,noise,parity error)
 *
 * @param[in] regBase: Uart register base address
 * @return None
 */
void UART_REG_ClearErrorFlags(UART_Type* regBase);

/*!
 * @brief Set uart rx idle enable
 *
 * @param[in] regBase: Uart register base address
 * @param[in] enable: Enable flag
 *            - true: Enable uart rx
 *            - false: Disable uart rx
 * @return None
 */
static inline void UART_REG_SetRxIdleEnable(UART_Type* regBase, bool enable)
{
    regBase->UCR = (regBase->UCR & ~UART_UCR_RIDLEN_Msk) | UART_CFG_RIDLE_ENABLE(enable ? 1U : 0U);
}
#if defined(__cplusplus)
}
#endif

#endif /* UART_REG_ACCESS_H */

/*******EOF********************************************************************/
